package pt.inescid.components.policyengine.heimdall.core;

import java.util.Vector;

import pt.inescid.components.policyengine.heimdall.base.EventObj;
import pt.inescid.components.policyengine.heimdall.base.User;
import pt.inescid.components.policyengine.heimdall.commons.Log;
import pt.inescid.components.policyengine.heimdall.commons.TimeUtilities;
import pt.inescid.components.policyengine.heimdall.contact.Outbound;
import pt.inescid.components.policyengine.heimdall.contact.RemoteCommand;
import pt.inescid.components.policyengine.heimdall.contact.RemoteResponse;
import pt.inescid.components.policyengine.heimdall.pap.PolicyManager;
import pt.inescid.components.policyengine.heimdall.pap.UserManagerAS;
import pt.inescid.components.policyengine.heimdall.pap.base.CesDefinition;
import pt.inescid.components.policyengine.heimdall.pap.base.ModelDefinition;
import pt.inescid.components.policyengine.heimdall.pap.base.ReevaluationDefinition;
import pt.inescid.components.policyengine.heimdall.pdp.PolicyDecision;
import pt.inescid.components.policyengine.heimdall.pdp.base.ModelInstance;
import pt.inescid.components.policyengine.heimdall.pdp.base.PurgeInstance;
import pt.inescid.components.policyengine.heimdall.pdp.base.ReevaluationInstance;
import pt.inescid.components.policyengine.heimdall.pip.PolicyPIP;

public class PolicyMonitor {

	
	
	
	/* ===========================================================
	==============================================================
	==============================================================
	storeEvent
	==============================================================
	==============================================================
	=========================================================== */
    public static void storeEvent(Vector newEventInfo) {

    	Log.debug(Log.INFO, "Store New Event.", "HeimdallServer.storeEvent");
    	
    	HeimdallServer server = HeimdallServer.getInstance();
		
    	EventObj ce = (EventObj) newEventInfo.elementAt(0);
    	User currentUser = (User) newEventInfo.elementAt(1);
    	String modelDefTag = (String) newEventInfo.elementAt(2);
    	
    	//Put to Policy information Point(PIP)
    	server.pip.storeEvent(
    			ce, 			// ce
    			currentUser,	// user
    			modelDefTag);	// tag
    	
    	System.out.println("Stored event: " + ce.getId());

    	// check if any leased event was confirmed
		ModelInstance currentModel = null;
    	ReevaluationInstance currentReevalInst = null;
    	for (int i = 0; i < server.eventsInLease.size(); i++) {
			currentModel = (ModelInstance) 	server.eventsInLease.elementAt(i);
			
			if (currentModel.getCe().getId() == ce.getId()) {

				server.eventsInLease.removeElementAt(i);
				i = server.eventsInLease.size();
			}

	    	System.out.println("Confirmed event: " + ce.getId() + " -> removed Lease");
 
	    	// check if a reevaluation must be created
	    	ModelDefinition currentModelDef = currentUser.getModelByTag(
	    			modelDefTag);
	    	Vector allReevalDefs = currentModelDef.getReevaluations();
	    	ReevaluationDefinition currentReevalDef = null;    	

	    	for (int j = 0; j < allReevalDefs.size(); j++) {
				currentReevalDef = (ReevaluationDefinition)
						allReevalDefs.elementAt(j);
				
				if (currentReevalDef.startsReevaluation(ce)) {
					currentReevalInst = new ReevaluationInstance(
							currentReevalDef, currentModel);
					
					server.eventsInReevaluation.add(currentReevalInst);

					Log.debug(Log.INFO, "Reevaluation START.", 
						"HeimdallServer.storeEvent");
				
					System.out.println("Reevaluation START.");
				}
			}
    	}
    	
    	
    	// checks if a reevaluation must be terminated
    	for (int i = 0; i < server.eventsInReevaluation.size(); i++) {
			currentReevalInst = (ReevaluationInstance)
					server.eventsInReevaluation.elementAt(i);
			currentModel = new ModelInstance(ce);
			
			if (currentReevalInst.stopsReevaluation(currentModel)) {
				Log.debug(Log.INFO, "Reevaluation STOP.", 
				"HeimdallServer.storeEvent");
		
				System.out.println("Reevaluation STOP.");

				server.eventsInReevaluation.removeElementAt(i);
			}
		}
    } // storeEvent

    
    
	/* ===========================================================
	==============================================================
	==============================================================
	checkRequests
	==============================================================
	==============================================================
	=========================================================== */
    public static boolean checkRequests() {

    	ReceptionQueueNode nextNode = null;
    	boolean someAction = false;
    	
    	while ((nextNode = (ReceptionQueueNode) 
    			QueueManager.getNextFromQueue(
    					QueueManager.RECEPTION_QUEUE, true)) != null) {
    		
    		if (nextNode.getType() ==
    			ReceptionQueueNode.REQUEST_STORE_EVENT) {
    			
    			storeEvent((Vector) nextNode.getRequest());
    		}
    		else if (nextNode.getType() ==
    			ReceptionQueueNode.REQUEST_EVALUATE_EVENT) {
    			
    			processEvent((ModelInstance) nextNode.getRequest());
    		}
    		else if (nextNode.getType() ==
    			ReceptionQueueNode.REQUEST_REMOTE_COMMAND) {
    			
    			processCommand((RemoteCommand) nextNode.getRequest());
    		}
    		else if (nextNode.getType() ==
    			ReceptionQueueNode.REQUEST_PROCESS_NEW_INFO) {
    			
    			newInfo((RemoteCommand) nextNode.getRequest());
    		}
    		
    		someAction = true;
    	}
    	
    	return (someAction);
    } // checkRequests    
    
    
	/* ===========================================================
	==============================================================
	==============================================================
	checkObligations
	==============================================================
	==============================================================
	=========================================================== */
    public static void checkObligations() {

    	Log.debug(Log.INFO, "Checking obligation fulfillment.", 
    			"HeimdallServer.checkObligations");
    	
    	HeimdallServer server = HeimdallServer.getInstance();
		
		ModelInstance currentModel = null;
		PolicyDecision currentDecision = null;
		for (int i = 0; i < server.eventsInObligation.size(); i++) {
    		currentModel = (ModelInstance) server.eventsInObligation.elementAt(i);
    		
    		currentDecision = currentModel.getDecision();
    		
    		if (currentDecision.getStatus() ==
    					PolicyDecision.STATUS_WAIT_OBLIG) {
    		
    		
    			if (currentModel.getObligationFulfillTimestamp() <
    					TimeUtilities.getCurrentTime()) {
    				
    				currentModel.evaluate();
    				
    				currentDecision = currentModel.getDecision();
    				
    				if (currentDecision.getStatus() ==
    					PolicyDecision.STATUS_FINAL) {
    					
    					if (currentDecision.getValue() ==
    						PolicyDecision.DECISION_AUTHORIZE) {
    						
    						Log.debug(Log.INFO, "FULFILLED Obligation.", 
    						"HeimdallServer.checkObligations");

    				    	System.out.println("FULFILLED Obligation.");
    				    	

    					}
    					else {
    						Log.debug(Log.INFO, "FAILED Obligation.", 
    						"HeimdallServer.checkObligations");

    				    	System.out.println("FAILED Obligation.");
    					}
    					
    					server.eventsInObligation.removeElementAt(i);
    					i--;
    				}
    				else {
        				currentModel.setObligationInfoRetrievalTimestamp(
        						TimeUtilities.getCurrentTime() + 
        						HeimdallServer.OBLIGATION_INFO_RETRIEVAL_TIMEOUT);
    				}
    			}	
    		}
    		
    		
    		else if (currentDecision.getStatus() ==
				PolicyDecision.STATUS_WAIT_OBLIG_INFO) {
	
    			
    			if (currentModel.getObligationInfoRetrievalTimestamp() <
    					TimeUtilities.getCurrentTime()) {
    				
    				currentModel.evaluate();
    				
    				currentDecision = currentModel.getDecision();
    				
    				if (
    						currentDecision.getStatus() ==
    					PolicyDecision.STATUS_FINAL &&
    					currentDecision.getValue() ==
    						PolicyDecision.DECISION_AUTHORIZE) {
    						
    						Log.debug(Log.INFO, "FULFILLED Obligation.", 
    						"HeimdallServer.checkObligations");

    				    	System.out.println("FULFILLED Obligation.");
   						
    				}
    				else {
    					Log.debug(Log.INFO, "FAILED Obligation.", 
    					"HeimdallServer.checkObligations");

				    	System.out.println("FAILED Obligation.");
   				}
    					
    				server.eventsInObligation.removeElementAt(i);
    				i--;
    			}						
    		}
		}
    } // checkObligations

    
    

	/* ===========================================================
	==============================================================
	==============================================================
	checkReevaluations
	==============================================================
	==============================================================
	=========================================================== */
    public static void checkReevaluations() {

    	Log.debug(Log.INFO, "Checking reevaluations.", 
    			"HeimdallServer. checkReevaluations");
    	
    	HeimdallServer server = HeimdallServer.getInstance();
		
		ReevaluationInstance currentReeval = null;
		PolicyDecision currentDecision = null;
		for (int i = 0; i < server.eventsInReevaluation.size(); i++) {
			currentReeval = (ReevaluationInstance) 
					server.eventsInReevaluation.elementAt(i);
    		
			if ((currentReeval.getLastReevaluationTime() +
   					currentReeval.getEvery()) <
    					TimeUtilities.getCurrentTime()) {
    				
   				currentReeval.getStartModel().evaluate();
    				
    			currentDecision = currentReeval.getStartModel().getDecision();
    				
    			if (currentDecision.getStatus() ==
    				PolicyDecision.STATUS_FINAL) {
    				
    				if (currentDecision.getValue() ==
    					PolicyDecision.DECISION_DENY) {
    					
    					server.eventsInReevaluation.removeElementAt(i);
        				i--;

        				Log.debug(Log.INFO, "FAILED reevaluation.", 
     						"HeimdallServer.checkReevaluations");
    					
    					System.out.println("FAILED reevaluation.");
    				}
    				else {
    					currentReeval.setLastReevaluationTime(
    							TimeUtilities.getCurrentTime());

    					Log.debug(Log.INFO, "Model REEVALUATED ok.", 
 						"HeimdallServer.checkReevaluations");
					
    					System.out.println("Model REEVALUATED ok.");
    				}
    			}
   			}	
		}
    } // checkReevaluations

    

    
    
	/* ===========================================================
	==============================================================
	==============================================================
	checkPurges
	==============================================================
	==============================================================
	=========================================================== */
    public static void checkPurges() {

    	Log.debug(Log.INFO, "Checking purges.", 
    			"HeimdallServer.checkPurges");
    	
		
 		Vector allUsers = UserManagerAS.getAllUsers();
		User currentUser = null;

		long currentTime = TimeUtilities.getCurrentTime();
    	
		for (int i = 0; i < allUsers.size(); i++) {
			currentUser = (User) allUsers.elementAt(i);
			
			Vector allModelDefs = currentUser.getModels();
			ModelDefinition currentModelDef = null;
			for (int j = 0; j < allModelDefs.size(); j++) {
				currentModelDef = (ModelDefinition) 
						allModelDefs.elementAt(j);
				
				Vector allCes = currentModelDef.getCes();
				CesDefinition currentCesDef = null;
				for (int p = 0; p < allCes.size(); p++) {
					currentCesDef = (CesDefinition) 
							allCes.elementAt(p);
					
					PurgeInstance currentPurgeInst =
						currentCesDef.getPurge();
					
					if (currentPurgeInst != null &&
							(currentPurgeInst.getLastPurgeTime() +
									currentPurgeInst.getEvery()) <
									currentTime) {
						
						currentCesDef.purge(
								currentUser,
								currentModelDef);
						
						currentPurgeInst.setLastPurgeTime(
								TimeUtilities.getCurrentTime());

    					Log.debug(Log.INFO, "Model PURGE.", 
 						"HeimdallServer.checkPurges");
					
    					System.out.println("Model PURGE.");
					}
				}
			}
		}
    } // checkPurges

    
    


	/* ===========================================================
	==============================================================
	==============================================================
	processEvent
	==============================================================
	==============================================================
	=========================================================== */
    public static void processEvent(ModelInstance currentModel) {
    	
    	Log.debug(Log.INFO, "Processing New Event.", 
    	"HeimdallServer.main");
    	
    	HeimdallServer server = HeimdallServer.getInstance();
		
		currentModel.evaluate();
    	
		PolicyDecision currentDecision = currentModel.getDecision();
		
    	if (currentDecision.getStatus() == PolicyDecision.STATUS_FINAL) {
    		
    		if (currentDecision.getValue() ==
    			PolicyDecision.DECISION_AUTHORIZE) {
    			
    			currentModel.setLeaseTimestamp(
    					TimeUtilities.getCurrentTime() +
    					HeimdallServer.LEASE_TIME);
    			QueueManager.addToQueue(currentModel, 
    					QueueManager.EVENTS_IN_LEASE_QUEUE);
    			
    			// if it is an obligation
    			// policy, activate it
    			if (currentModel.getDefinition().
    					isObligation()) {
    				
    				currentDecision.setStatus(
    						PolicyDecision.STATUS_WAIT_OBLIG);
    				
    				currentModel.setObligationFulfillTimestamp(
    						TimeUtilities.getCurrentTime() + 
    						currentModel.getDefinition().getObligationTimeout());
    				
    				server.eventsInObligation.add(
    						currentModel);
    				
    				Log.debug(Log.INFO, "ACTIVATED Obligation.", 
    				"HeimdallServer.main");
			
    				System.out.println("ACTIVATED Obligation.");
    			}
    			
    			Log.debug(Log.INFO, "AUTHORIZED Model into " +
    					"EVENTS_IN_LEASE_QUEUE.", 
    			"HeimdallServer.main");
    		}
    		else {
    			QueueManager.addToQueue(currentModel, 
    					QueueManager.PROCESSED_EVENTS_QUEUE);
    			
    			if (currentDecision.getValue() == PolicyDecision.DECISION_DENY) {
    				
    				Log.debug(Log.INFO, "DENIED Model into PROCESSED_EVENTS_QUEUE.", 
    				"HeimdallServer.main");
    			}
    			else if (currentDecision.getValue() == PolicyDecision.DECISION_INFLUENCED_BY_LEASE) {
    				
    				Log.debug(Log.INFO, "INFLUENCED_BY_LEASE Model into PROCESSED_EVENTS_QUEUE.", 
    				"HeimdallServer.main");
    			}
    			else {
    				
    				Log.debug(Log.INFO, "OTHER REASONS Model into PROCESSED_EVENTS_QUEUE.", 
    				"HeimdallServer.main");
    			}
    		}
    	}
    	else {
    		QueueManager.addToQueue(currentModel, 
    				QueueManager.EVENTS_IN_PROCESS_QUEUE);
    		
    		Log.debug(Log.INFO, "Model into IN_PROCESS_QUEUE.", 
    		"HeimdallServer.main");
    	}
    	
    	Log.debug(Log.INFO, "Processing finished: " +
    			currentModel.getDecision(), 
    	"HeimdallServer.main");

   
    } // processEvent
    
    
    
    
    
	/* ===========================================================
	==============================================================
	=============                    =============================
	=============      processCommand       =============================
	=============                    =============================
	==============================================================
	=========================================================== */
    public static void processCommand(RemoteCommand command) {
		Log.debug(Log.INFO, "Arrived.", "InboundThread.processCommand");
   	
		RemoteResponse response = null;
		
		// process the request
		switch (command.getType()) {
		case RemoteCommand.LOAD_CES_COMMAND:
			response = PolicyManager.exportCes(command);
			break;
		case RemoteCommand.QUERY_CES_COMMAND:
			response = PolicyPIP.queryCes(command);
			
			break;
		case RemoteCommand.REMOTE_EVALUATION_COMMAND:
			response = remoteEvaluation(command);
			break;
		case RemoteCommand.TEST_COMMAND:
			response = test(command);
			break;
			
		default:
			break;
		}
		
		Vector allParameters = new Vector();
		allParameters.add(response);
		
		RemoteCommand responseCommand = new RemoteCommand(
				RemoteCommand.RESPONSE_COMMAND, allParameters);
		
		responseCommand.setHostname(command.getHostname());
		responseCommand.setHostport(command.getHostport());
		
		Outbound.sendResponse(response);
    }
    

    
    
    
	/* ===========================================================
	==============================================================
	=============                    =============================
	=============      remoteEvaluation       =============================
	=============                    =============================
	==============================================================
	=========================================================== */
	public static RemoteResponse remoteEvaluation(
			RemoteCommand command) {

		Log.debug(Log.INFO, "Arrived", "InboundThread.remoteEvaluation");

		// parameters received:
		// - user id
		// - ce
		// - model def tag
		// - policyName
		Vector allParameters = command.getMessage();
		long userId = ((Long) allParameters.elementAt(0)).longValue();
		EventObj ce = (EventObj) allParameters.elementAt(1);
		String modelDefTag = (String) allParameters.elementAt(2);
		String modelInstTag = (String) allParameters.elementAt(2);
		String policyName = (String) allParameters.elementAt(3);

		// verify if the user exists
		User currentUser = UserManagerAS.getRecord(userId);
		if (currentUser == null) {
			return (new RemoteResponse(
					RemoteResponse.UNKNOWN_USER_RESPONSE,
					null));
		}
		
		Log.debug(Log.INFO, "User exists.", 
				"InboundThread.remoteEvaluation");

		// get the policy
		ModelInstance currentModel = 
			currentUser.getModelInstance(ce, modelDefTag);
		currentModel.evaluate(policyName);
		
		// reply to policy home host
		Vector allResponseParameters = new Vector();
		allResponseParameters.add(modelInstTag);
		allResponseParameters.add(policyName);
		allResponseParameters.add(currentModel.getDecision());

		Log.debug(Log.INFO, "Finished.", "InboundThread.remoteEvaluation");

		return (new RemoteResponse(
				RemoteResponse.REMOTE_EVALUATION_RESPONSE,
				allResponseParameters));
	}
	
	

	
	/* ===========================================================
	==============================================================
	=============                    =============================
	=============      test       =============================
	=============                    =============================
	==============================================================
	=========================================================== */
	public static RemoteResponse test(RemoteCommand command) {

		try {
			Thread.sleep(3000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		return (new RemoteResponse(
				RemoteResponse.TEST_RESPONSE,
				null));
	}


    
    

	/* ===========================================================
	==============================================================
	==============================================================
	newInfo
	==============================================================
	==============================================================
	=========================================================== */
    public static void newInfo(RemoteCommand responseCommand) {
    	
		Log.debug(Log.INFO, "New Info Arrived.", "HeimdallServer.main");
		System.out.println("NEW INFO.");
		
		RemoteResponse response = 
			(RemoteResponse) 
			((Vector) responseCommand.getMessage()).elementAt(0);
		
		Vector allParameters = response.getMessage();

    	HeimdallServer server = HeimdallServer.getInstance();
		
		ModelInstance currentModel = null;
		
		// update PIP cache
		server.pip.updateCache(response);
		
		// get the processing model instance
		boolean found = false;
		for (int i = 0; i < server.eventsInProcess.size(); i++) {
			currentModel = (ModelInstance) 
					server.eventsInProcess.elementAt(i);
			
			if (currentModel.getTag().equals(
					(String) allParameters.elementAt(1))) {
				
				found = true;
				i = server.eventsInProcess.size();
			}
		}
		
		
		
		for (int i = 0; i < server.eventsInObligation.size(); i++) {
			currentModel = (ModelInstance) 
					server.eventsInObligation.elementAt(i);
			
			if (currentModel.getTag().equals(
					(String) allParameters.elementAt(1))) {
				
				found = true;
				i = server.eventsInObligation.size();
			}
		}
		
		// if not found, then it corresponds to an
		// evaluation which was already terminated, 
		// and the new info can be discarded
		PolicyDecision currentDecision = null;
		if (found) {
			Log.debug(Log.INFO, "Updating evaluation.", 
			"HeimdallServer.main");
			
			if (response.getType() == RemoteResponse.QUERY_CES_RESPONSE) {
				currentModel.updateEventInfo(allParameters);
			}
			else {
				currentModel.updateEvaluationInfo(allParameters);
			}
			
			if (currentModel.getDecision().getStatus() !=
				PolicyDecision.STATUS_WAIT_OBLIG_INFO) {
				currentModel.evaluate();
				
				currentDecision = currentModel.getDecision();
				
				if (currentDecision.getStatus() ==
					PolicyDecision.STATUS_FINAL) {
					
					if (currentDecision.getValue() ==
						PolicyDecision.DECISION_AUTHORIZE) {
						
						currentModel.setLeaseTimestamp(
								TimeUtilities.getCurrentTime());
						QueueManager.addToQueue(currentModel, 
								QueueManager.EVENTS_IN_LEASE_QUEUE);
						
						// if it is an obligation
						// policy, activate it
						if (currentModel.getDefinition().
								isObligation()) {
							
							server.eventsInObligation.add(
									currentModel);
							
							Log.debug(Log.INFO, "ACTIVATED Obligation.", 
							"HeimdallServer.main");

					    	System.out.println("ACTIVATED Obligation.");
}
						
						Log.debug(Log.INFO, "AUTHORIZED Model into " +
								"EVENTS_IN_LEASE_QUEUE.", 
						"HeimdallServer.main");
					}
					else {
						QueueManager.addToQueue(currentModel, 
								QueueManager.PROCESSED_EVENTS_QUEUE);
						
						if (currentDecision.getValue() ==
							PolicyDecision.DECISION_DENY) {
							
							Log.debug(Log.INFO, "DENIED Model into PROCESSED_EVENTS_QUEUE.", 
							"HeimdallServer.main");
						}
						else if (currentDecision.getValue() ==
							PolicyDecision.DECISION_INFLUENCED_BY_LEASE) {
							
							Log.debug(Log.INFO, "INFLUENCED_BY_LEASE Model into PROCESSED_EVENTS_QUEUE.", 
							"HeimdallServer.main");
						}
						else {
							
							Log.debug(Log.INFO, "OTHER REASONS Model into PROCESSED_EVENTS_QUEUE.", 
							"HeimdallServer.main");
						}
					}
					
					Log.debug(Log.INFO, "Model into PROCESSED_EVENTS_QUEUE.", 
					"HeimdallServer.main");
					
					System.out.println("-->2:" + 
							currentModel.toString());
				}
				else {
					QueueManager.addToQueue(currentModel, 
							QueueManager.EVENTS_IN_PROCESS_QUEUE);
					
					Log.debug(Log.INFO, "Model into IN_PROCESS_QUEUE.", 
					"HeimdallServer.main");
				}
				
				Log.debug(Log.INFO, "Processing finished: " +
						currentModel.getDecision(), 
				"HeimdallServer.main");
			}
		}
		else {
			Log.debug(Log.INFO, "Model not found, " +
					"discarding newInfo.", 
			"HeimdallServer.main");
		}
    } // newInfo
    
} // PolicyMonitor
